home *** CD-ROM | disk | FTP | other *** search
-
- IPC.DOC (DRES.LIBRARY)
-
- Matthew Dillon
- 25 September 1988
-
-
- Special note. I've tried my best, but this document is somewhat
- lacking in the exact mechanics and implementation of IPC. Please
- refer to the source module to resolve such questions.
-
- The IPC section of the DRES.LIBRARY pertains to the sending of commands
- and retrieval of results between arbitrary processes without conflict.
- Processes which use IPC come in three flavors:
-
- (1) A process which is able to accept and process IPC commands
- (2) A process which might send IPC commands to other processes
- (3) A process which does both.
-
- The IPC mechanism implemented by this library is inherently asychronous
- in nature (messages and ports), but also provides a synchronous call
- specially tailored for cases (2) and (3). Doing IPC commands synchronously
- has certain advantages, the greatest of which is that the implementation by
- application software is greatly simplified. The synchronous call gets
- around possible deadlocks in case (3) by interrupting itself and calling a
- handler if incomming messages occur while it is waiting for the sent
- message to complete.
-
- The greatest feature of this IPC mechanism is a great flexibility in
- who allocates and deallocates the message buffers. Both the source and
- destination processes have a wide range of options concerning message
- buffers. Unless otherwise specified, an allocated copy of the message
- buffer is automatically made and used which allows the send or reply message
- buffer used by the process to be immediately discarded. Additionaly, a
- destination IPCPort can be flagged to force incomming messages to be
- allocated no matter what the sender requests.
-
- NOTE: The term 'allocation of messages' inherently means that if the
- message is allocated, the user of the message can munge it however he likes
- (exception: IF_GLOBAL messages that are propogated through all IPC ports).
- Allocated messages are automatically FreeMem()d by the IPC subsystem or
- by the receiver. Also, WHEN DUPLICATION OF MESSAGES OCCUR, THE ALLOCATED
- MEMORY IS OF THE SAME TYPE AS THE ORIGINAL BUFFER.
-
- NOTE: Do not get confused by the many different ways messages buffers
- can be allocated. A message buffer will never be allocated-duplicate more
- than once. For example, if the source specifies that it is passing an
- allocated buffer, the further duplication is done by the IPC subsystem.
-
- -The sender can allocate the message buffer himself and pass that,
- pass a temporary buffer which the IPC subsystem duplicates, or pass
- a static buffer to the destination without the overhead of allocation
- and duplication.
-
- -The receiver of a particular message can determine whether that message
- was allocated and FreeMem() it himself, can allow the IPC subsystem
- to free allocated messages, and can even force all incomming messages
- on his IPC port to be allocated no matter what the sender specifies.
- Forcing the IPC subsystem to allocate otherwise static messages is
- a very useful ability to have. For instance, the parser employed by
- the receiver of a message might destroy or modify the message buffer
- during parser, while the original sender of the message might have
- used a static string or otherwise expected the buffer not to be
- screwed up. By the receiver being able to specify that he WANTS the
- buffer to be an allocated duplicate because he intends to modify it
- without permission is a good thing. Even though the sender passes
- a static buffer to the IPC subsystem, that subsystem will allocate
- and duplicate it for the receiver.
-
- -In his reply to the sender, the receiver has the same sort of control
- as sender in terms of how the reply message buffer is allocated. After
- getting the reply back, the original sender can determine whether the
- reply buffer was allocated and FreeMem() it himself, or allow the IPC
- subsystem to free it when he is through with the reply.
-
- -Finally, the receiver of the original message can even free his own
- reply buffer when the original sender is through with it!
-
- So in general, one can use either the IPC subsystem's automatic buffer
- copying feature (the default), use no buffer copying at all, or use one's
- own scheme.
-
- MOST OF THESE ALLOCATION FEATURES ARE IMPLEMENTED THROUGH FLAGS. READ
- THE DOCS BELOW ON OPENIPC() and SENDIPC().
-
-
- APPLICATION NAME DOMAINS
-
- An application for cases (1) or (3) must create a receiver port for
- IPC messages with the OpenIPC() call. OpenIPC() takes a name and flags
- argument. The Flags argument is discussed in OpenIPC() below. You do
- not need a port to send messages, only to receive them.
-
- The name of the port determines the type of messages it will receive.
- The format is: <application_name>.<domain> , for example: "dmouse.CMD"
- Currently, only the .CMD domain has a defined standard. It does not matter
- whether multiple copies of the application are running, they can share the
- same port name. You could have, say, two invocations of your favorite
- editor each with five active projects. Support for this is discussed in
- the ReplyIPC() call below.
-
- While this port exists, other tasks may send IPC messages to it. You
- must extract the messages (GetMsg()), process it, and then ReplyIPC() the
- message to return it to the original sender.
-
- The CloseIPC() call will shutdown an IPC port that you had previously
- openning and return any pending messages by trying to pass them to the
- next application of the same name (if none, an error is eventually returned
- to the sender).
-
- .CMD ports
-
- Messages sent over .CMD ports are formatted as null-terminated
- ascii-text. The send buffer (msg->TBuf) is formatted as follows:
-
- project\0command\0 (\0 = NUL = ascii 0)
-
- The \0 MUST be included as part of the buffer and buffer length. The
- project name allows the application to identify which of its possibly
- multiple projects is to be affected by the command. a NULL project name
- (\0command\0) is allowed and refers to the 'active' project in the
- application. The project name is ignored by applications which do not
- support the notion of projects. The format of the command depends on the
- application, but must be in ascii and terminate with a \0 (ascii 0).
-
- The logical extension for the specification of multiple project names
- is to allow comma delimited names and wildcards (* and ?). For example,
- "*\0command\0" would refer to all projects, "a.c,b.c\0command\0" would
- refer to two specific projects. This might not be supported by all
- applications.
-
- The format of the reply buffer depends on the success of the operation.
- If the command was successful, the reply buffer will either be NULL or
- contain some ascii/text response ending in \0. If the command was not
- successful, IF_ERROR will be set in msg->RFlags and the reply buffer will
- be either NULL or contain some ascii/text error message ending in \0.
- The msg->Error integer error code will be non-zero if IF_ERROR was set,
- possibly non-zero if IF_ERROR was not set to indicate a warning (IF_ERROR
- always means Total-Failure). If msg->Error is not set by the application
- which returns the error condition, it will be set by the IPC subsystem.
-
-
-
- ParseCmd ParseCmd
-
- argc = ParseCmd(buf, &argv, varget, varfree, &error, NULL);
- int argc;
- char **argv; (note, address passed)
- char *((*varget)());
- void (*varfree)();
- long error; (note, address passed)
-
- (note: Last NULL argument (0L) is required for future compatibility).
-
- THIS COMMAND CAN EASILY BE USED WITH NON-IPC RELATED ROUTINES!
-
- Do DME-type parsing on a string buffer. The buffer is terminated with
- a \0 but may contain a \n at the end (which is ignored) to be compatible
- with fgets() as well as gets(). NOTE that if the string is terminated
- with a \<LF><NUL> i.e. "\\n\0", the backslash will override the standard
- ignorance of the \n and parse it into the result.
-
- DME-type parsing delimites fields by spaces except those enclosed in
- matching parenthesis. Multiple levels of parenthesis is tracked and
- the OUTERMOST SET REMOVED. Other special characters include shift-6
- (^), backslash (\), and dollar ($):
-
- form examples function
-
- ^<char> ^c ^d ^L embed a control character, case is ignored
- you may not embed ^@ (0)
-
- \<char> \\ \( \^ override the meaning of the next character,
- allows embedding special characters.
-
- $(varname) inline string variable, var name can be
- anything except ')'.
-
- $varname inline string variable, var name
- restricted to alphanumeric and '_'.
-
- Example: "echo (this (is a) test) $file/x a mess ^g \(hi! \)ug\\"
- Result: argv[0] = "echo"
- argv[1] = "this (is a) test"
- argv[2] = "dnet:foo/x" (assumes $file == 'dnet:foo')
- argv[3] = "a"
- argv[4] = "mess"
- argv[5] = "<CONTROL-G>" (i.e. ascii code 7)
- argv[6] = "(hi!"
- argv[7] = ")ug\"
- argv[8] = NULL
-
- Note: offset -1 of each argument contains a status byte. Currently,
- bit 0 is defined to indicate whether the argument was paren'd or not.
- This cannot otherwise be told as the highest level of parenthesis are
- stripped. (argv[0][-1] & 1) == 0, (argv[1][-1] & 1) == 1.
-
- --------
-
- buf -\0 or \n\0 terminated buffer holding the command to parse.
- The buffer is not modified and may be discarded after the
- call returns.
-
- &argv -address of a variable which will be initialized to point
- to an allocated array of pointers to strings exactly the
- way the main() argv works.
-
- varget -Pointer to a subroutine which when given a \0 terminated
- variable name will return a \0 terminated string or NULL
- if that variable does not exist.
-
- varget(varname)
- char *varname;
-
- This argument may be NULL indicating string variables are
- not supported. The routine may return non-allocated
- strings (i.e. static storage or string constants) in which
- case there is no need for a varfree() function.
-
- varfree -Pointer to a subroutine which when given the string
- pointer returned by varget() will free it. This call
- is made immediately after varget() is called and returns
- nothing. You may pass NULL for this argument if no free
- function exists (varget was NULL or returned static
- strings).
-
- &error -You must supply a pointer to a longword which will be
- returned holding the error, if any, and the index where
- the error occured, if any. The index is stored in the
- lower 16 bits and the error # stored in the upper 16 bits:
-
- 1 << 16 = NO MEMORY
- 2 << 16 = STRING VARIABLE NOT FOUND (varget() returned
- NULL), the lower 16 bits contain the index into
- buf.
-
- NULL -This argument is reserved for future expansion and must
- be NULL (0L).
-
- NOTE: The returned arg list may be munged however you wish, including
- the pointers themselves, just as long as you pass the original
- argv pointer (i.e. the entries may be munged) to FreeParseCmd().
-
- 0 is always returned on error, and error will be set properly.
- Otherwise, argc is returned and error will hold 0.
-
- The original buffer is not modified in any way.
-
- NOTE: Since the outer set of parenthesis () are stripped, offset -1
- of each arguments provides information as to whether the
- argument was quoted or not. If bit 0 is set, the argument
- was quoted, I.E.: (a) b c
- (av[0][-1] & 1) = 1 av[0] = "a"
- (av[1][-1] & 1) = 0 av[1] = "b"
- (av[2][-1] & 1) = 0 av[2] = "c"
-
-
- FreeParseCmd FreeParseCmd
-
- (void) FreeParseCmd(argv)
-
- This routine frees the storage referenced by the argv... the argv array
- and the original strings are all freed. It is ok if Individual entries
- in the argv array are munged.
-
-
- OpenIPC OpenIPC
-
- port = OpenIPC(name, flags)
- PORT *port;
- char *name;
- long flags;
-
- Create an IPC port of the specified name suitable for receiving IPC
- commands on. If the port could not be created due to lack of memory,
- NULL is returned, else an EXEC port is returned. The port is setup
- as PA_SIGNAL and takes up a signal bit in your signal set. If the
- application does not wish a signal-port, it may FreeSignal() the
- signal and change the flags to something other than PA_SIGNAL.
-
- Due to the fact that the signal was allocated for the calling task,
- the port is normally usable only from the calling task. One can
- change the ownership and port characterstics but always remember that
- CloseIPC() can only be called from the owning task.
-
- IF_ALLOC If IF_ALLOC is specified, *ALL* incomming message buffers
- will be forced to be allocated even if the sender of the
- message specifies IF_NOCOPY. I.E. you, as the receiver of
- IPC messages on this port can specify this flag to guarentee
- the msg->TBuf is allocated and thus modify the contents
- of the buffer itself without confusing the original sender
- (who may have specified a static string)
-
- Note: However, if this message is to be passed on via
- IF_GLOBAL or IF_NOTFND, you probably do not want to munge
- the buffer.
-
- IF_GLOBAL If IF_GLOBAL is specified, this port will accept global
- messages. Global messages are always of the .CMD form
- though the sender could easily put stuff beyond the second
- \0 in the message buffer destined for specialized routines.
-
- If not specified, IF_GLOBAL messages will NEVER propogate
- through this port.
-
-
- CloseIPC CloseIPC
-
- (void) CloseIPC(port)
- PORT *port;
-
- Delete an IPC port previously obtained by OpenIPC(). The owning task
- must make this call since it will FreeSignal() the allocated signal.
- Note that if the port flags are set to something other than PA_SIGNAL,
- no attempt will be made to free the possibly non-existant signal bit.
-
- This call will pass any pending messages on to the next application
- of the same name, and if no other exists, will return the messages
- with an error. Pending messages with IF_GLOBAL set continue their
- journey through the IPC ports flagged to accept IF_GLOBAL messages.
-
-
- SendIPC SendIPC
-
- msg = SendIPC(appname, buf, len, flags)
- IPCMSG *msg;
- char *appname;
- APTR buf;
- long len, flags;
-
- This routine starts an asynchronous IPC request. It allocates and
- initializes an IPCMSG and reply port and sends the message to the
- destination application (appname). The IPCMSG is returned and it's
- msg->RFlags holds the IF_ALLOCMSG internal flag to cause the IPCMSG
- and replyport to be deallocated when you are through with the reply.
- NOTE! The returned IPCMSG has already been sent to the destination
- and is not owned by you. The idea is to then CheckMsg() and/or
- WaitMsg() on it. The ANode filed of the IPCMSG, however, *IS* owned
- by you and you may use it however you wish.
-
- The buffer and buffer length you pass SendIPC() is used to allocate
- a copy of the buffer (which is deallocated automatically when the
- destination ReplyIPC()s). This means that you can discard/reuse your
- buffer as soon as the call returns. However, if you specify the
- IF_NOCOPY flag SendIPC() will use your buffer pointer and you must
- be sure not to modify its contents until the message is returned to you.
-
- If you specify IF_ALLOC it is assumed your buffer pointer and length
- were AllocMem()d and the system will FreeMem() them when the destination
- is through processing your message and calls ReplyIPC().
-
- ** See SendIPC2() for info on how to handle the message when it comes
- back.
-
- * If the 'name' is NULL, the first application in the application
- list is called. This is usually used in conjunction with IF_GLOBAL
- to indicate that the message should propogate to all IPC ports in the
- system.
-
- SendIPC2 SendIPC2
-
- msg = SendIPC2(name, msg)
-
- This routine is called by SendIPC(). This assumes you already have an
- IPC message structure that you wish to use. The msg argument is
- returned. The reply fields, including msg->Error and msg->Confirm,
- are zerod.
-
- The IPCMSG must be initialized as follows:
-
- msg->Msg.mn_ReplyPort -must hold reply port for the message
- msg->Msg.mn_Length -must hold length of IPCMSG structure ONLY
- if you had specified the IF_ALLOCMSG in
- msg->TFlags
- msg->Msg.TBuf -holds the buffer pointer to the message
- or NULL
- msg->Msg.TLen -holds the length of the message
- msg->Msg.TFlags -holds flags associated with the message:
- IF_ALLOC * automatic deallocation on reply. This flag
- is set automatically if you do not specify
- IF_NOCOPY. If NEITHER IF_NOCOPY or IF_ALLOC
- are specified, the IPC subsystem will
- allocate a duplicate of your buffer, place
- it in TBuf, and set this flag for you.
- IF_GLOBAL * force propogate to all applications in
- the system before returning to the sender.
- IF_NOCOPY * force IPC system to use the buffer you
- placed in TBuf. Otherwise, the IPC system
- will allocate and copy your buffer into
- its own and place that in TBuf.
-
- IF_ALLOCMSG * IPC system will deallocate the IPCMSG
- itself when FreeIPC() is called. This
- flag is set automatically when the IPC
- system has allocate the IPCMSG in the
- first place.
-
- WARNING! Upon return, msg->TBuf will not necessarily point to your
- buffer, even if you specified IF_NOCOPY or IF_ALLOC.
-
- Handling the message when it comes back
-
- After you make the call, you must wait for the message to complete
- via WaitMsg and/or CheckMsg(). Note that WaitMsg() will remove the
- message from its reply port. If you do not use WaitMsg(), you must
- remember to remove the message before continuing.
-
- Then, process the reply (msg->RBuf, msg->RLen, and msg->RFlags).
- The msg->RBuf IS ALLOWED TO BE NULL. msg->RFlags will indicate
- any errors:
-
- IF_ERROR|IF_NOTFND|IF_NOAPP: no application of that name exists
-
- IF_ERROR|IF_NOTFND : application exists, but the requested
- project does not exist. OR the
- command does not exist for this
- application.
-
- IF_ERROR : The application exists and can handle
- the project & command, but an error
- occured during interpretation of the
- command.
-
- IF_ALLOC : The reply buffer was allocated. You
- have the option of FreeMem()ing it
- yourself or allowing the system to
- do so. If you do it, you must set
- RBuf = NULL.
-
- NOTE: msg->Error does not need to be set by the receiver. If
- msg->Error is 0 and IF_ERROR is set, msg->Error will be set to
- 20 by the IPC subsystem.
-
- NOTE: In the case of IF_ERROR, the reply buffer might still be
- non-NULL and contain an error message.
-
- When through dealing with the reply, you MUST call FreeIPC(msg). This
- routine will deallocate the reply buffer if msg->RFlags had IF_ALLOC
- set by the destination and also free the message it self if msg->TFlags
- contained IF_ALLOCMSG set by SendIPC() or the user before a SendIPC2().
-
- Additionaly, if msg->Confirm was set by the destination, this function
- vector will be called with the message pointer as an argument to allow
- the destination to deallocate its reply. NOTE!!! If the message has
- IF_GLOBAL set in msg->TFlags, msg->Confirm should NOT be touched by
- receiving applications.
-
-
- DoIPC2 DoIPC2
-
- (void) DoIPC2(name, msg, collfunc, collport)
- long flags
- char *name;
- IPCMSG *msg;
- void (*collfunc)();
- PORT *collport;
-
- This is the hall mark of the IPC library and allows one to send
- SEMI-SYNCHRONOUS messages while still retaining the ability to
- process incomming messages on your own IPC port (case #3). The IPC
- message msg must be setup as in SendIPC2(). The collision function
- and collision port may be NULL if you wish.
-
- This command sends the message and then waits for the reply. If while
- waiting for the reply a message comes in on the collision port (usually
- your IPC port), the collision vector will be called with the collision
- port as an argument, allowing you to process incomming messages while
- waiting for one you sent. By properly designing your software, you
- can allow multiply stacked DoIPC2() calls.
-
- When the message finally comes back, this call returns. The application
- must still deal with its reply buffer and call IPCFree() when through.
-
- NOTE: The collision function is called as a subroutine of this routine
- and thus if it is a general command-handling routine remember that
- your command interpreter might have to be synchronously reentrant.
- DoIPC2() is certainly reentrant. A4 and A5 are not screwed up by
- DoIPC2() so you do not have to reload your data segment base register
- from the collision vector routine if using the small data model.
- (Compatible with both Aztec and Lattice C).
-
-
- ReplyIPC ReplyIPC
-
- (void) ReplyIPC(msg, buf, len, flags)
- IPCMSG *msg;
- APTR buf; (may be NULL & len would then be 0)
- long len;
- long flags;
-
- You must reply to all IPC messages you receive on your IPC port by
- calling ReplyIPC(). ReplyIPC() works almost exactly like IPCSend(),
- but deals with the RBuf, RFlags, and RLen fields of the IPC structure.
- Normally, the IPC system will allocate a duplicate of your reply buffer
- and stick that in msg->RBuf which allows you to throw away your buffer
- after calling ReplyIPC(). Various flags override this feature:
-
- IF_ALLOC - The buffer is automatically deallocated when the
- source calls FreeIPC(). If this flag exists the
- IPC system will not allocate a duplicate buffer.
-
- IF_NOCOPY - The IPC system will not allocate a duplicate buffer.
-
- Normally, (1) neither of these flags is specified, or (2) IF_ALLOC
- is specified. Specifying IF_ALLOC alone means that the IPC system
- assumes you have AllocMem()d the buffer yourself and want it
- automatically freed when the source (original sender) calls
- FreeIPC().
-
- Specifying both IF_ALLOC and IF_NOCOPY is equivalent to specifying
- just IF_ALLOC. Specifying just IF_NOCOPY means that the IPC system
- will use your buffer pointer and will NOT attempt to deallocate it.
- In this case, you are responsible for keeping the buffer intact
- until the source calls FreeIPC(). This is possible through the
- use of the msg->Confirm vector. ** you cannot use msg->Confirm
- for IF_GLOBAL packets! Because there is only one vector entry and
- possibly multiple users of the message.
-
- NOTE: The buffer pointer placed in msg->RBuf will either be yours
- or the allocated duplicate. msg->RBuf will be set to NULL when (if)
- the system deallocates it.
-
- If you specify IF_NOTFND, your buffer and length are completely
- ignored and the message is passed on to the next application program
- with the same name. IF THE ORIGINATOR SPECIFIED IF_GLOBAL WHEN HE
- SENT THE MESSAGE, AND YOU DID NOT SPECIFY IF_NOTFND, the message
- will be passed on to the next application anyway and your reply will be
- passed on along with it. In the same way, if you were not the first
- application to get this message (it was passed to you), the
- msg->RBuf/RLen/RFlags fields may already have something in them. If
- your reply buffer pointer is not the same address as the one already in
- msg->RBuf and msg->RFlags held IF_ALLOC, the old buffer in msg->RBuf
- will be deallocated before processing continues with your ReplyIPC().
-
- ** Warning, msg->RBuf will be replaced in the same manner msg->TBuf
- is by an allocated duplicate unless you specify otherwise.
-
-
- FreeIPC FreeIPC
-
- (void) FreeIPC(msg)
- IPCMSG *msg;
-
- This routine must be called when the original sender of an IPC message
- has sent the message, waited for the reply, removed the message from
- the reply port, and processed the reply buffer. This serves to free
- the message and any associated buffers as specified below:
-
- If msg->RFlags has IF_ALLOC set the reply buffer is immediately
- deallocated and msg->RBuf set to NULL.
-
- If msg->Confirm is non-NULL the vector will be called with the IPCMSG
- as an argument.
-
- If msg->TFlags holds IF_ALLOCMSG, the msg will be FreeMem()d with the
- length msg->Msg.mn_Length. IF_ALLOCMSG is not usually set by the
- user... it is set by SendIPC() which allocates a message/replyport
- and wants both to be deallocated on completion.
-
-
-
-
-